//
//  Copyright 2011  comphilip
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using Dict4Engkoo.Result;
using Newtonsoft.Json.Linq;
using System.Text;

namespace Dict4Engkoo.Command {
	internal class MatchCommand : ICommand, IAsyncCommand {
		private IResult _result = null;
		private Action<IResult> _resultDelegate = null;
		private DictContext _dictContext = null;
		private HttpWebRequest _request = null;

		public IResult Process(DictContext Context) {
			return EndProcess(BeginProcess(Context, null, null));
		}

		public IAsyncResult BeginProcess(DictContext Context, AsyncCallback Callback, object State) {
			_resultDelegate = AsyncResult;
			_dictContext = Context;
			//MATCH database strategy word
			if (Context.CommandParameters.Length != 3)
				return _resultDelegate.BeginInvoke(CommonResults.IllegalParameters, Callback, State);
			string[] databases = new string[] { "engkoo", "!", "*" };
			if (!databases.Contains(Context.CommandParameters[0], StringComparer.OrdinalIgnoreCase))
				return _resultDelegate.BeginInvoke(new StatusTextResponse(550, StatusArgument.CreateRawStringArgument(Strings.InvalidDatabase)), Callback, State);

			string[] supportStrats = new string[] { "exact", "prefix", "." };
			if (!supportStrats.Contains(Context.CommandParameters[1], StringComparer.OrdinalIgnoreCase))
				return _resultDelegate.BeginInvoke(new StatusTextResponse(551, StatusArgument.CreateRawStringArgument(Strings.InvalidStrategy)), Callback, State);

			_resultDelegate = null;
			return BeginGetSuggestions(Context.CommandParameters[2], Callback, State);
		}

		public IResult EndProcess(IAsyncResult AR) {
			if (_resultDelegate == null && _request == null)
				throw new InvalidOperationException();
			try{
				if (_resultDelegate != null) {
					_resultDelegate.EndInvoke(AR);
					return _result;
				} else {
					try {
						bool oneResult = _dictContext.CommandParameters[0].Equals("*", StringComparison.OrdinalIgnoreCase);
						bool prefix = _dictContext.CommandParameters[1].Equals("prefix", StringComparison.OrdinalIgnoreCase);
						var result = from x in EndGetSuggestions(AR, _dictContext.CommandParameters[2])
									 where(prefix && x.StartsWith(_dictContext.CommandParameters[2], StringComparison.OrdinalIgnoreCase)) ||
									 string.Equals(_dictContext.CommandParameters[2], x, StringComparison.OrdinalIgnoreCase)
									 select x;
						if (result.Count() <= 0)
							return new StatusTextResponse(552, StatusArgument.CreateRawStringArgument(Strings.NoMatchStatusText));
	
						StringBuilder resultBuilder = new StringBuilder();
						if (oneResult)
							resultBuilder.AppendLine(string.Format("engkoo {0}", StatusArgument.CreateRawStringArgument(result.First())));
						else {
							foreach (string item in result)
								resultBuilder.AppendLine(string.Format("engkoo {0}", StatusArgument.CreateQuoteStringArgument(item)));
						}
	
						return new CompositeResult() {
							new StatusTextResponse(152, StatusArgument.CreateRawStringArgument(string.Format(Strings.DefinitionsRetrievedText, oneResult ? 1 : result.Count()))),
								new TextResponse(resultBuilder.ToString()),
								CommonResults.OK
						};
					} catch (WebException exp) {
						return CommonResults.FormatException(exp);
					}
				}
			}finally{
				_dictContext = null;
				_result = null;
				_resultDelegate = null;
				_request = null;
			}
		}

		private IAsyncResult BeginGetSuggestions(string Word, AsyncCallback Callback, object State) {
			HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://dict.bing.com.cn/io.aspx");
			request.Method = "POST";
			request.ContentType = "application/x-www-form-urlencoded";
			using(StreamWriter requestWriter = new StreamWriter(request.GetRequestStream())) {
				requestWriter.WriteLine("q={0}&t=sug&ut=0&ulang=ZH-CN&tlang=EN-US", Uri.EscapeDataString(Word));
			}
			
			_request = request;
			return request.BeginGetResponse(Callback, State);
		}
		
		private IEnumerable<string> EndGetSuggestions(IAsyncResult AR, string Word){
			using(WebResponse response = _request.EndGetResponse(AR))
			using(Stream responseStream = response.GetResponseStream())
			using(StreamReader reader = new StreamReader(responseStream)) {
				string json = reader.ReadToEnd();
				JObject rootObject = JObject.Parse(json);
				JToken token = rootObject["ACS"]["AC"];
				if (!(token is JContainer))
					return new string[0];

				if (token is JObject) {
					JArray array = new JArray();
					array.Add(token);
					token = array;
				}

				return from x in token.Cast<JToken>()
					   let y = Uri.UnescapeDataString(x.Value<string>("$"))
							   select y.Replace(string.Format("{{#*{0}*$}}", Word), Word);
			}
		}
		
		private void AsyncResult(IResult Result) {
			_result = Result;
		}
	}
}

